﻿// Original author contact info: Owen Emlen (owene_1998@yahoo.com)
// Note: other individuals may also have contributed to this code
// Project hosted on CodePlex.com as of 1/10/2009 at http://wpfimageviewer.codeplex.com
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Threading;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Windows.Data;
using System.Windows.Shapes;
using System.ComponentModel;

namespace BrainTechLLC.WPF
{
    public static class TempExten
    {
        public static void HandleChange<T1, T2>(this DependencyObject d, DependencyPropertyChangedEventArgs e, Action<T1, T2> action, Action<T1> update) where T1 : DependencyObject
        {
            T1 ctrl = (T1)d;
            T2 val = (T2)e.NewValue;
            action(ctrl, val);
            update(ctrl);
        }

        public static T FindParent<T>(this FrameworkElement t) where T : FrameworkElement
        {
            if (t == null)
                return null;

            DependencyObject d = VisualTreeHelper.GetParent(t);

            if (d != null)
            {
                T ret = d as T;

                if (ret == null)
                    return (d as FrameworkElement).FindParent<T>();

                return ret;
            }

            return null;
        }

        public static void StretchListBox(this FrameworkElement t)
        {
            ContentPresenter p = t.FindParent<ContentPresenter>();

            if (p != null)
            {
                p.HorizontalAlignment = HorizontalAlignment.Stretch;
            }
        }

        public static void StretchListBoxBothWays(this FrameworkElement t)
        {
            ContentPresenter p = t.FindParent<ContentPresenter>();

            if (p != null)
            {
                p.HorizontalAlignment = HorizontalAlignment.Stretch;
                p.VerticalAlignment = VerticalAlignment.Stretch;
            }
        }

        public static bool EnsureVisibility(this UIElement c, bool visible)
        {
            if (c == null)
                return false;

            if (visible && c.Visibility == Visibility.Collapsed)
            {
                c.Visibility = Visibility.Visible;
                return true;
            }
            else if (!visible && c.Visibility == Visibility.Visible)
            {
                c.Visibility = Visibility.Collapsed;
                return true;
            }

            return false;
        }


        public static List<T> GetAllChildrenNonDependencyObject<T>(this DependencyObject obj, bool recursive) where T : class
        {
            List<T> results = new List<T>();

            for (int n = 0; n < VisualTreeHelper.GetChildrenCount(obj); n++)
            {
                var child1 = VisualTreeHelper.GetChild(obj, n);

                if (child1 != null)
                {
                    if (child1 is T)
                        results.Add(child1 as T);

                    if (recursive)
                    {
                        results.AddRange(GetActualChildrenNonDependencyObject<T>(child1 as DependencyObject));
                    }
                }
            }

            return results;
        }

        public static List<T> GetActualChildrenNonDependencyObject<T>(this DependencyObject p) where T : class
        {
            List<T> results = new List<T>();

            if (p == null)
                return results;

            if (p is Border)
            {
                var b = p as Border;
                if (b.Child != null)
                {
                    if (b.Child is T)
                        results.Add(b.Child as T);

                    results.AddRange(GetActualChildrenNonDependencyObject<T>(b.Child as DependencyObject));
                }
            }
            else if (p is Canvas)
            {
                results.AddRange((p as Canvas).GetAllChildrenNonDependencyObject<T>(true));
            }
            else if (p is ItemsPresenter)
            {
                ItemsPresenter ip = p as ItemsPresenter;
                results.AddRange(GetAllChildrenNonDependencyObject<T>(ip, true));
            }
            else if (p is Panel)
            {
                results.AddRange((p as Panel).GetAllChildrenNonDependencyObject<T>(true));
            }
            else if (p is ContentControl)
            {
                var cc = p as ContentControl;
                if (cc.Content != null && cc.Content is DependencyObject)
                {
                    if (cc.Content is T)
                        results.Add(cc.Content as T);

                    results.AddRange(GetActualChildrenNonDependencyObject<T>(cc.Content as DependencyObject));
                }
                else
                {
                    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(cc); i++)
                    {
                        var item = VisualTreeHelper.GetChild(cc, i);
                        results.AddRange(GetActualChildrenNonDependencyObject<T>(item as DependencyObject));
                    }
                }
            }
            else if (p is ScrollContentPresenter)
            {
                var sp = p as ScrollContentPresenter;

                if (sp.Content != null && sp.Content is DependencyObject)
                {
                    if (sp.Content is T)
                        results.Add(sp.Content as T);
                    results.AddRange(GetActualChildrenNonDependencyObject<T>(sp.Content as DependencyObject));
                }
            }
            else if (p is ContentPresenter && (p as ContentPresenter).Content is DependencyObject)
            {
                ContentPresenter cp = p as ContentPresenter;
                if (cp.Content != null)
                {
                    if (cp.Content is T)
                        results.Add(cp.Content as T);

                    results.AddRange(GetActualChildrenNonDependencyObject<T>(cp.Content as DependencyObject));
                }
            }
            else
            {
                if (p is T)
                    results.Add(p as T);

                results.AddRange(p.GetAllChildrenNonDependencyObject<T>(true));
            }

            return results;
        }

        public static List<T> GetAllChildren<T>(this DependencyObject obj, bool recursive) where T : DependencyObject
        {
            List<T> results = new List<T>();

            for (int n = 0; n < VisualTreeHelper.GetChildrenCount(obj); n++)
            {
                var child1 = VisualTreeHelper.GetChild(obj, n);

                if (child1 != null)
                {
                    if (child1 is T)
                        results.Add(child1 as T);

                    if (recursive)
                    {
                        results.AddRange(GetActualChildren<T>(child1 as DependencyObject));
                    }
                }
            }

            return results;
        }

        public static List<T> GetActualChildren<T>(this DependencyObject p) where T : DependencyObject
        {
            List<T> results = new List<T>();

            if (p == null)
                return results;

            if (p is Border)
            {
                var b = p as Border;
                if (b.Child != null)
                {
                    if (b.Child is T)
                        results.Add(b.Child as T);

                    results.AddRange(GetActualChildren<T>(b.Child as DependencyObject));
                }
            }
            else if (p is Canvas)
            {
                results.AddRange((p as Canvas).GetAllChildren<T>(true));
            }
            else if (p is ItemsPresenter)
            {
                ItemsPresenter ip = p as ItemsPresenter;
                results.AddRange(GetAllChildren<T>(ip, true));
            }
            else if (p is Panel)
            {
                results.AddRange((p as Panel).GetAllChildren<T>(true));
            }
            else if (p is ContentControl)
            {
                var cc = p as ContentControl;
                if (cc.Content != null && cc.Content is DependencyObject)
                {
                    if (cc.Content is T)
                        results.Add(cc.Content as T);

                    results.AddRange(GetActualChildren<T>(cc.Content as DependencyObject));
                }
                else
                {
                    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(cc); i++)
                    {
                        var item = VisualTreeHelper.GetChild(cc, i);
                        results.AddRange(GetActualChildren<T>(item as DependencyObject));
                    }
                }
            }
            else if (p is ScrollContentPresenter)
            {
                var sp = p as ScrollContentPresenter;

                if (sp.Content != null && sp.Content is DependencyObject)
                {
                    if (sp.Content is T)
                        results.Add(sp.Content as T);
                    results.AddRange(GetActualChildren<T>(sp.Content as DependencyObject));
                }
            }
            else if (p is ContentPresenter && (p as ContentPresenter).Content is DependencyObject)
            {
                ContentPresenter cp = p as ContentPresenter;
                if (cp.Content != null)
                {
                    if (cp.Content is T)
                        results.Add(cp.Content as T);

                    results.AddRange(GetActualChildren<T>(cp.Content as DependencyObject));
                }
            }
            else
            {
                if (p is T)
                    results.Add(p as T);

                results.AddRange(p.GetAllChildren<T>(true));
            }

            return results;
        }

        public static int ForEachRecursive<T>(this DependencyObject p, Action<T> action, Func<T, bool> filter) where T : class
        {
            int count = 0;

            if (p == null)
                return 0;

            if (p is Canvas)
                return ForEachRecursive<T>(p as Canvas, action, filter);
            else if (p is Panel)
                return ForEachRecursive<T>(p as Panel, action, filter);

            try
            {
                var results = p.GetActualChildren<DependencyObject>();

                for (int n = 0; n < results.Count; n++)
                {
                    var child1 = results[n];

                    count += ForEachRecursive<T>(child1, action, filter);
                }
            }
            catch { }

            return 0;
        }

        public static int ForEachRecursive<T>(this Canvas p, Action<T> action, Func<T, bool> filter) where T : class
        {
            int count = 0;

            if (p == null)
                return 0;

            if (p is T)
            {
                if (filter == null || !filter(p as T))
                {
                    action(p as T);
                    count++;
                }
            }

            for (int n = 0; n < p.Children.Count; n++)
            {
                var child = p.Children[n];

                if (child is T)
                {
                    if (filter == null || !filter(child as T))
                    {
                        action(child as T);
                        count++;
                    }
                }

                Panel panelChild = child as Panel;
                if (panelChild != null)
                {
                    count += ForEachRecursive<T>(panelChild, action, filter);
                }
                else
                {
                    Canvas can = child as Canvas;

                    if (can != null)
                        count += ForEachRecursive<T>(can, action, filter);
                }

                ContentControl content = child as ContentControl;
                if (content != null && content.Content is Panel)
                {
                    count += ForEachRecursive<T>(content.Content as Panel, action, filter);
                }

                ContentPresenter presenter = child as ContentPresenter;
                if (presenter != null && presenter.Content is Panel)
                {
                    count += ForEachRecursive<T>(presenter.Content as Panel, action, filter);
                }
            }
            return count;
        }

        public static int ForEachRecursive<T>(this Panel p, Action<T> action, Func<T, bool> filter) where T : class
        {
            int count = 0;

            if (p == null)
                return 0;

            if (p is T)
            {
                if (filter == null || !filter(p as T))
                {
                    action(p as T);
                    count++;
                }
            }

            for (int n = 0; n < p.Children.Count; n++)
            {
                var child = p.Children[n];

                if (child is T)
                {
                    if (filter == null || !filter(child as T))
                    {
                        action(child as T);
                        count++;
                    }
                }

                Panel panelChild = child as Panel;
                if (panelChild != null)
                {
                    count += ForEachRecursive<T>(panelChild, action, filter);
                }
                else
                {
                    Canvas can = child as Canvas;

                    if (can != null)
                        count += ForEachRecursive<T>(can, action, filter);
                }

                ContentControl content = child as ContentControl;
                if (content != null && content.Content is Panel)
                {
                    count += ForEachRecursive<T>(content.Content as Panel, action, filter);
                }

                ContentPresenter presenter = child as ContentPresenter;
                if (presenter != null && presenter.Content is Panel)
                {
                    count += ForEachRecursive<T>(presenter.Content as Panel, action, filter);
                }

                ListBox lb = child as ListBox;
                if (lb != null)
                {
                    count += ForEachRecursive<T>(lb as Control, action, filter);
                }
            }
            return count;
        }

#if SILVERLIGHT
#else
        public static void RunOnDispatcher(this Dispatcher dispatcher, Delegate d, DispatcherPriority pri, params object[] param)
        {
            if (dispatcher == null)
            {
                d.DynamicInvoke(param);
            }
            else
            {
                dispatcher.BeginInvoke(d, pri, param);
            }
        }
#endif

#if SILVERLIGHT
#else

        public static bool IsElementVisibleInScrollRegion(this UIElement element, ScrollViewer scrollviewer)
        {
            GeneralTransform childTransform = element.TransformToAncestor(scrollviewer);
            Rect rectangle = childTransform.TransformBounds(new Rect(new System.Windows.Point(0, 0), element.RenderSize));

            //Check if the elements Rect intersects with that of the scrollviewer's
            Rect result = Rect.Intersect(new Rect(new System.Windows.Point(0, 0), scrollviewer.RenderSize), rectangle);

            return (result != Rect.Empty);
        }
#endif

#if SILVERLIGHT
#else
        public static System.Windows.Point GetAbsoluteLocationInScrollViewer(this Visual visual, ScrollViewer scrollViewer)
        {
            try
            {
                GeneralTransform gt = visual.TransformToVisual(scrollViewer);
                return gt.Transform(new System.Windows.Point(0, 0));
            }
            catch { return new System.Windows.Point(); }
        }
#endif
    }

    public static class UtilWPF
    {
        public static Point EmptyPoint = new Point(-1.99D, -1.99D);

        public static Visibility GetVisibility(this bool b) { return b ? Visibility.Visible : Visibility.Collapsed; }

        public static bool WithinWidthHeightTolerance(double width, double height, double oldWidth, double oldHeight)
        {
            double widthDifference = Math.Abs(oldWidth - width);
            double heightDifference = Math.Abs(oldHeight - height);

            return (widthDifference <= 8D && heightDifference <= 8D);
        }

        public static bool WithinBounds(this double val, double low, double high)
        {
            return (val >= low && val <= high);
        }

        public static void GetScrollViewerInfo(this ScrollViewer scrollViewer, out double viewportWidth, out double viewportHeight, out double verticalOffset)
        {
            viewportWidth = double.MaxValue;
            viewportHeight = double.MaxValue;
            verticalOffset = 0D;

            if (scrollViewer != null)
            {
                viewportWidth = scrollViewer.ViewportWidth;
                viewportHeight = scrollViewer.ViewportHeight;
                verticalOffset = scrollViewer.VerticalOffset;
            }
        }

#if SILVERLIGHT
#else
        /// <summary>
        /// Gets the location of the dependency object relative to the scroll viewer
        /// </summary>
        /// <param name="dep"></param>
        /// <param name="scrollViewer"></param>
        /// <returns></returns>
        public static Point GetLocation(this DependencyObject dep, ScrollViewer scrollViewer)
        {
            if (scrollViewer == null)
                scrollViewer = (dep as FrameworkElement).FindParent<ScrollViewer>();

            System.Windows.Point p = new System.Windows.Point(0, 0);

            if (scrollViewer != null)
            {
                try
                {
                    GeneralTransform gt = (dep as Visual).TransformToVisual(scrollViewer);
                    p = gt.Transform(new System.Windows.Point(0, 0));
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                    return EmptyPoint;
                }
            }

            return p;
        }

        public static bool? GetIsVisibleInScroller(this DependencyObject dep, ScrollViewer scrollViewer, double viewportWidth, double viewportHeight, double verticalOffset)
        {
            if (scrollViewer == null)
                scrollViewer = (dep as FrameworkElement).FindParent<ScrollViewer>();

            System.Windows.Point p = new System.Windows.Point(0, 0);

            if (scrollViewer != null)
            {
                try
                {
                    GeneralTransform gt = (dep as Visual).TransformToVisual(scrollViewer);
                    p = gt.Transform(new System.Windows.Point(0, 0));
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                    Thread.Sleep(100);
                    return null;
                }
            }

            double thisActualHeight = (double)dep.GetValue(Control.ActualHeightProperty);

            if (p.Y + thisActualHeight >= 0)
            {
                return (p.X < viewportWidth && (p.Y + thisActualHeight + 16D) >= 0 && (p.Y - 10D) <= viewportHeight);
            }
            return false;
        }
#endif

#if SILVERLIGHT
#else

        public static string CalculateMD5Hash(this byte[] bytes)
        {
            MD5 md5 = new MD5CryptoServiceProvider();
            byte[] retVal = md5.ComputeHash(bytes);
            ASCIIEncoding enc = new ASCIIEncoding();
            return enc.GetString(retVal);
        }
#endif

        public static List<T> GetSelected<T>(this ListBox listBox) where T : class
        {
            List<T> results = new List<T>();

            if (listBox == null || listBox.SelectedItems == null)
                return results;

            foreach (var item in listBox.SelectedItems)
            {
                var vm = item as T;
                results.Add(vm);
            }

            return results;
        }
    }

    public static class Filtering
    {
#if SILVERLIGHT
#else
        public static void Filter(this ItemsControl lv, Predicate<object> filter)
        {
            if (lv.ItemsSource == null) return;
            ICollectionView dataView = CollectionViewSource.GetDefaultView(lv.ItemsSource);
            dataView.Filter = filter;
            dataView.Refresh();
        }
#endif
    }

    public static class Sorting
    {
#if SILVERLIGHT
#else
        private static Dictionary<ItemsControl, GridViewColumnHeader> LastHeadersClicked = new Dictionary<ItemsControl, GridViewColumnHeader>();
#endif
        private static Dictionary<ItemsControl, ListSortDirection> LastDirection = new Dictionary<ItemsControl, ListSortDirection>();

        private static Dictionary<CollectionViewSource, string> LastSort = new Dictionary<CollectionViewSource, string>();

        public static void Sort(this CollectionViewSource lv, string sortBy, ListSortDirection direction)
        {
            ICollectionView dataView = lv.View;

            dataView.SortDescriptions.Clear();
            SortDescription sd = new SortDescription(sortBy, direction);
            dataView.SortDescriptions.Add(sd);
            dataView.Refresh();
        }

        public static void HandleSorting(this CollectionViewSource source, string thisSort, string path, ListSortDirection direction)
        {
            Sort(source, path, direction);
            LastSort[source] = thisSort;
        }

#if SILVERLIGHT
#else
        public static void HandleColumnHeaderClick(this ItemsControl lst, GridViewColumnHeader headerClicked)
        {
            DataTemplate dt1 = CreateSortDataTemplate("M 5,10 L 15,10 L 10,5 L 5,10");
            DataTemplate dt2 = CreateSortDataTemplate("M 5,5 L 10,10 L 15,5 L 5,5");

            ListSortDirection direction;
            GridViewColumnHeader lastHeaderClicked = null;
            ListSortDirection lastDirection;

            LastHeadersClicked.TryGetValue(lst, out lastHeaderClicked);

            if (!LastDirection.TryGetValue(lst, out lastDirection))
            {
                lastDirection = ListSortDirection.Ascending;
            }

            if (headerClicked != null && headerClicked.Role != GridViewColumnHeaderRole.Padding)
            {
                direction = (headerClicked != lastHeaderClicked) ? ListSortDirection.Ascending :
                            ((lastDirection == ListSortDirection.Ascending) ? ListSortDirection.Descending : ListSortDirection.Ascending);

                string bindingPath;

                Binding displayBinding = headerClicked.Column.DisplayMemberBinding as Binding;

                if (displayBinding != null)
                {
                    bindingPath = (headerClicked.Column.DisplayMemberBinding as Binding).Path.Path as string;
                }
                else
                {
                    bindingPath = headerClicked.Tag as string;
                }

                Sort(lst, bindingPath, direction);

                if (direction == ListSortDirection.Ascending)
                {
                    headerClicked.Column.HeaderTemplate = dt1;
                }
                else
                {
                    headerClicked.Column.HeaderTemplate = dt2;
                }

                // Remove arrow from previously sorted header
                if (lastHeaderClicked != null && lastHeaderClicked != headerClicked)
                {
                    lastHeaderClicked.Column.HeaderTemplate = null;
                }

                LastHeadersClicked[lst] = headerClicked;
                LastDirection[lst] = direction;
            }
        }
#endif

#if SILVERLIGHT
#else
        private static DataTemplate CreateSortDataTemplate(string path)
        {
            DataTemplate dt = new DataTemplate();
            FrameworkElementFactory dpFactory = new FrameworkElementFactory(typeof(DockPanel));
            dt.VisualTree = dpFactory;
            FrameworkElementFactory tbFactory = new FrameworkElementFactory(typeof(TextBlock));
            tbFactory.SetBinding(TextBlock.TextProperty, new Binding());
            dpFactory.AppendChild(tbFactory);
            GeometryConverter gc = new GeometryConverter();
            FrameworkElementFactory pathFactory = new FrameworkElementFactory(typeof(Path));
            pathFactory.SetValue(Path.StrokeThicknessProperty, 1.0D);
            pathFactory.SetValue(Path.FillProperty, Brushes.Gray);
            pathFactory.SetValue(Path.DataProperty, gc.ConvertFrom(path) as Geometry);
            dpFactory.AppendChild(pathFactory);
            return dt;
        }
#endif


#if SILVERLIGHT
#else
        public static void Sort(this ItemsControl lv, string sortBy, ListSortDirection direction)
        {
            ICollectionView dataView = CollectionViewSource.GetDefaultView(lv.ItemsSource);

            dataView.SortDescriptions.Clear();
            SortDescription sd = new SortDescription(sortBy, direction);
            dataView.SortDescriptions.Add(sd);
            dataView.Refresh();
        }
#endif
    }

}